package org.dlow.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.dlow.annotation.RpcReference;
import org.dlow.common.URL;
import org.dlow.common.constants.Register;
import org.dlow.common.constants.RpcProxy;
import org.dlow.event.RpcListerLoader;
import org.dlow.filter.FilterFactory;
import org.dlow.proxy.ServiceProxyProvider;
import org.dlow.proxy.ProxyProviderFactory;
import org.dlow.register.RegistryProviderFactory;
import org.dlow.register.ServiceRegistry;
import org.dlow.router.ServiceSelectorFactory;
import org.dlow.socket.client.Client;
import org.dlow.socket.serialization.SerializationFactory;
import org.dlow.tolerant.FailureHandlerFactory;

import java.lang.reflect.Field;

/**
 * ConsumerPostProcessor - RPC客户端Bean后置处理器
 * 
 * <p>这是RPC客户端与SpringBoot集成的核心类,实现了Spring的BeanPostProcessor和InitializingBean接口。
 * 负责在SpringBoot应用启动时初始化RPC客户端组件,并为带有@RpcReference注解的字段注入代理对象。
 * 
 * <h3>核心职责:</h3>
 * <ul>
 *   <li><b>初始化RPC客户端组件</b>: 在Spring容器初始化完成后,加载并初始化各种SPI扩展(代理工厂、负载均衡器等)</li>
 *   <li><b>启动Netty客户端</b>: 启动网络通信客户端,用于接收服务端响应</li>
 *   <li><b>扫描@RpcReference注解</b>: 扫描所有Bean的字段,找到带有@RpcReference注解的字段</li>
 *   <li><b>生成并注入代理对象</b>: 为RPC服务接口生成代理对象,并注入到字段中</li>
 *   <li><b>订阅服务</b>: 从注册中心订阅服务,获取服务提供者列表</li>
 * </ul>
 * 
 * <h3>工作流程:</h3>
 * <pre>
 * 1. SpringBoot启动
 *    ↓
 * 2. 执行afterPropertiesSet()方法
 *    ├─ 初始化事件监听器(RpcListerLoader)
 *    ├─ 初始化容错工厂(FailureHandlerFactory)
 *    ├─ 初始化注册中心工厂(RegistryProviderFactory)
 *    ├─ 初始化客户端过滤器(FilterFactory)
 *    ├─ 初始化代理工厂(ProxyProviderFactory)
 *    ├─ 初始化负载均衡工厂(ServiceSelectorFactory)
 *    ├─ 初始化序列化工厂(SerializationFactory)
 *    └─ 启动Netty客户端(Client)
 *    ↓
 * 3. Spring创建各个Bean
 *    ↓
 * 4. 执行postProcessBeforeInitialization()方法
 *    ├─ 遍历Bean的所有字段
 *    ├─ 发现@RpcReference注解的字段
 *    ├─ 使用代理工厂生成代理对象
 *    ├─ 从注册中心订阅服务
 *    └─ 将代理对象注入到字段中
 *    ↓
 * 5. Bean初始化完成,可以调用远程服务
 * </pre>
 * 
 * <h3>使用示例:</h3>
 * <pre>
 * // 在Controller或Service中使用@RpcReference
 * &#64;RestController
 * public class UserController {
 *     
 *     // 此字段会被ConsumerPostProcessor处理
 *     &#64;RpcReference
 *     private UserService userService;
 *     
 *     &#64;GetMapping("/user/{id}")
 *     public User getUser(Long id) {
 *         // userService已经是代理对象,调用方法会发起RPC请求
 *         return userService.getUserById(id);
 *     }
 * }
 * </pre>
 * 
 * <h3>关键技术点:</h3>
 * <ul>
 *   <li><b>BeanPostProcessor</b>: Spring提供的Bean后置处理器接口,可以在Bean初始化前后进行自定义处理</li>
 *   <li><b>InitializingBean</b>: Spring提供的初始化接口,在Bean属性设置完成后执行afterPropertiesSet方法</li>
 *   <li><b>反射机制</b>: 使用反射遍历Bean的字段,检查注解,并注入代理对象</li>
 *   <li><b>动态代理</b>: 使用CGLib生成服务接口的代理对象,拦截方法调用并转为RPC请求</li>
 * </ul>
 * 
 * @description: 客户端Bean后置处理器,负责初始化RPC客户端并注入代理对象
 * @Author: 山沐与山
 * @CreateTime: 2024-05-17 14:57
 * @see BeanPostProcessor Spring Bean后置处理器接口
 * @see InitializingBean Spring初始化接口
 * @see RpcReference 服务引用注解
 */
public class ConsumerPostProcessor implements BeanPostProcessor, InitializingBean {

    RpcProperties rpcProperties;

    public ConsumerPostProcessor(RpcProperties rpcProperties) {
        this.rpcProperties = rpcProperties;
    }

    /**
     * Bean属性设置完成后的初始化方法
     * 
     * <p>此方法在Spring容器完成依赖注入后自动调用,负责初始化RPC客户端的所有组件。
     * 初始化顺序很重要,确保各组件的依赖关系正确。
     * 
     * <h3>初始化步骤:</h3>
     * <ol>
     *   <li>初始化事件监听器: 用于监听服务注册、更新、下线等事件</li>
     *   <li>初始化容错工厂: 加载SPI配置的容错策略(Failover、Failfast等)</li>
     *   <li>初始化注册中心工厂: 加载SPI配置的注册中心实现(Zookeeper等)</li>
     *   <li>初始化客户端过滤器: 加载用户自定义的客户端过滤器</li>
     *   <li>初始化代理工厂: 加载SPI配置的代理实现(CGLib等)</li>
     *   <li>初始化负载均衡工厂: 加载SPI配置的负载均衡算法(轮询、随机等)</li>
     *   <li>初始化序列化工厂: 加载SPI配置的序列化实现(JDK、Hessian等)</li>
     *   <li>启动Netty客户端: 启动网络通信客户端,准备接收服务端响应</li>
     * </ol>
     * 
     * <p><b>注意:</b> Netty客户端启动后会一直运行,用于接收异步响应。
     * 
     * @throws Exception 初始化过程中可能抛出的异常
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // 1. 初始化事件监听器,用于监听服务变化
        new RpcListerLoader().init();
        // 2. 初始化容错策略工厂,加载Failover、Failfast等策略
        FailureHandlerFactory.init();
        // 3. 初始化注册中心工厂,加载Zookeeper等注册中心实现
        RegistryProviderFactory.init();
        // 4. 初始化客户端过滤器,加载用户自定义的前置/后置过滤器
        FilterFactory.initClient();
        // 5. 初始化代理工厂,加载CGLib等代理实现
        ProxyProviderFactory.init();
        // 6. 初始化负载均衡工厂,加载轮询、随机等负载均衡算法
        ServiceSelectorFactory.init();
        // 7. 初始化序列化工厂,加载JDK、Hessian等序列化实现
        SerializationFactory.init();
        // 8. 启动Netty客户端,用于接收服务端响应
        final Client client = new Client();
        client.run();
    }

    /**
     * Bean初始化前的后置处理方法
     * 
     * <p>这是整个RPC客户端与SpringBoot集成的核心方法。
     * 在每个Bean初始化之前,扫描其所有字段,找到带有@RpcReference注解的字段,
     * 为其生成代理对象并注入,实现透明的远程服务调用。
     * 
     * <h3>处理流程:</h3>
     * <pre>
     * 对于每个Bean:
     *   遍历所有字段
     *     ↓
     *   发现@RpcReference注解?
     *     ↓ 是
     *   获取字段类型(服务接口)
     *     ↓
     *   获取@RpcReference注解的配置(版本号、超时等)
     *     ↓
     *   使用代理工厂生成代理对象
     *     ↓
     *   构造服务URL(服务名+版本号)
     *     ↓
     *   从注册中心订阅服务(获取提供者列表)
     *     ↓
     *   将代理对象注入到字段中
     *     ↓
     *   完成,该字段现在可以调用远程服务了
     * </pre>
     * 
     * <h3>关键技术:</h3>
     * <ul>
     *   <li><b>反射机制</b>: 通过反射获取字段、检查注解、设置字段值</li>
     *   <li><b>动态代理</b>: 使用CGLib为服务接口生成代理对象</li>
     *   <li><b>服务订阅</b>: 从注册中心获取服务提供者列表,支持服务发现</li>
     * </ul>
     * 
     * <h3>为什么使用代理?</h3>
     * <p>代理对象拦截方法调用,将本地方法调用转换为RPC请求:
     * <ol>
     *   <li>用户调用: userService.getUserById(123)</li>
     *   <li>代理拦截: 拦截getUserById方法调用</li>
     *   <li>构造请求: 将方法名、参数等封装为RPC请求</li>
     *   <li>选择服务器: 使用负载均衡算法选择一个服务提供者</li>
     *   <li>发送请求: 通过Netty发送请求到服务端</li>
     *   <li>等待响应: 阻塞等待或异步等待服务端响应</li>
     *   <li>返回结果: 将响应结果返回给用户</li>
     * </ol>
     * 
     * @param bean 正在初始化的Bean实例
     * @param beanName Bean的名称
     * @return 处理后的Bean实例(可能注入了代理对象)
     * @throws BeansException 处理过程中的异常
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 1. 获取Bean的所有字段(包括private字段)
        final Field[] fields = bean.getClass().getDeclaredFields();
        
        // 2. 遍历所有字段,寻找带有@RpcReference注解的字段
        for (Field field : fields) {
            if(field.isAnnotationPresent(RpcReference.class)){
                // 3. 获取注册中心服务(Zookeeper)
                final ServiceRegistry registryService = RegistryProviderFactory.get(Register.ZOOKEEPER);
                
                // 4. 获取字段类型(服务接口类型,如UserService.class)
                final Class<?> aClass = field.getType();
                
                // 5. 获取@RpcReference注解,读取配置(版本号、超时时间、负载均衡策略等)
                final RpcReference rpcReference = field.getAnnotation(RpcReference.class);
                
                // 6. 设置字段可访问(即使是private字段也能设置)
                field.setAccessible(true);
                Object object = null;
                
                try {
                    // 7. 获取代理工厂(CGLib)
                    final ServiceProxyProvider iproxy = ProxyProviderFactory.get(RpcProxy.CG_LIB);
                    
                    // 8. 为服务接口生成代理对象,代理对象会拦截所有方法调用
                    final Object proxy = iproxy.createProxy(aClass,rpcReference);
                    object = proxy;
                    
                    // 9. 构造服务URL(包含服务名和版本号)
                    final URL url = new URL();
                    url.setServiceName(aClass.getName());
                    url.setVersion(rpcReference.version());
                    
                    // 10. 从注册中心订阅服务,获取服务提供者列表
                    // 订阅后,当服务提供者发生变化时,会自动更新本地缓存
                    registryService.subscribeService(url);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                
                try {
                    // 11. 将生成的代理对象注入到字段中
                    // 此时,字段从null变成了一个代理对象
                    field.set(bean,object);
                    
                    // 12. 恢复字段的访问控制
                    field.setAccessible(false);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        
        // 13. 返回处理后的Bean(字段可能已被注入代理对象)
        return bean;
    }
}
